﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace scanner_neb
{
    class Program
    {
        static Nebulas.Neb neblocal = new Nebulas.Neb("http://localhost:8685");

        static Nebulas.GetBlockByHeightOptions op = new Nebulas.GetBlockByHeightOptions();
        static void Main(string[] args)
        {
            Random r = new Random();
            byte[] pri = new byte[32];
            r.NextBytes(pri);
            Nebulas.Account ac = new Nebulas.Account(pri);
            ac.SetPrivateKey("ea00e3419fd7d6c53a065f22734c032731fb64be85d15dee77500a01a9939a9f");
            var wif=ac.GetPrivateKeyString();
            var addr = ac.GetAddressString();
            Console.WriteLine("neb addr scaner");
            Start();
            while (true)
            {
                Console.Write(">");
                var l = Console.ReadLine();
                if (l == "exit")
                {
                    return;
                }
            }
        }
        static async Task<int> GetLocalHeight()
        {
            var state = await neblocal.Api.GetNebState();
            var json = Newtonsoft.Json.Linq.JObject.Parse(state);
            int height = int.Parse(json["result"]["height"].ToString());
            return height;
        }
        static async Task<int> GetRemoteHeight()
        {
            System.Net.WebClient wc = new System.Net.WebClient();
            var state = await wc.DownloadStringTaskAsync("https://data.nebulas.io/block/max");
            var json = Newtonsoft.Json.Linq.JObject.Parse(state);
            int height = (int)float.Parse(json["data"]["height"].ToString());
            return height;
        }

        static uint GetCurCount()
        {
            if (System.IO.File.Exists("count.txt"))
            {
                var count = uint.Parse(System.IO.File.ReadAllText("count.txt"), System.Globalization.NumberStyles.HexNumber);
                return count;
            }
            return 0;
        }
        static void SetCurCount(uint count)
        {
            System.IO.File.WriteAllText("count.txt", count.ToString("X08"));
        }

        static async void Start()
        {
            int localheight = await GetLocalHeight();
            int remoteheight = await GetRemoteHeight();
            Console.WriteLine("remoteHeight=" + remoteheight + " localHeight=" + localheight);

            op.height = 10032;
            op.full_fill_transaction = true;

            Dictionary<string, bool> addrs = new Dictionary<string, bool>();
            List<string> lines = new List<string>();

            uint curCount = GetCurCount();
            if (curCount == 0)
                curCount = 1;
            DateTime time = DateTime.Now;
            DateTime time2 = time;//每10s log一次
            while (true)
            {

                if (curCount < localheight)
                {
                    try
                    {
                        await GetAddrOnBlock(curCount, addrs, lines);
                    }
                    catch (Exception err)
                    {
                        Console.WriteLine("got net error:" + err.Message);
                        //try
                        //{
                        //    client.Stop();
                        //}
                        //catch
                        //{

                        //}

                        //client = null;
                        continue;
                    }
                    curCount++;

                    var perblockfile = 0x10000;//64k
                    //地址太少了，改64k一格吧 //64k存一格文件
                    if (curCount != 0 && curCount % perblockfile == 0)//6w清理一次地址缓存
                    {
                        //并刷一下高度
                        localheight = await GetLocalHeight();
                        //serverCount = await client.GetBlockCountAsync();

                        addrs.Clear();

                        Console.Clear();
                        Console.WriteLine("Finish " + curCount.ToString("X08"));
                    }
                    if (curCount % 0x200 == 0)//512 log 一次
                    {
                        //time log
                        DateTime time1 = DateTime.Now;
                        Console.WriteLine("blockcount=" + curCount + "/" + localheight + " delta=" + (time1 - time).TotalSeconds);
                        time = time1;
                        time2 = time1;

                        //限制一下去重集的总大小，最大16M条记录
                        if (addrs.Count > 0x1000000)
                            addrs.Clear();
                        //addr log
                        var filename = "addrs_neb_" + ((curCount / perblockfile) * perblockfile).ToString("X08") + ".txt";
                        System.IO.File.AppendAllLines(filename, lines.ToArray());
                        lines.Clear();
                        //height  log
                        SetCurCount(curCount);
                    }
                    DateTime times = DateTime.Now;
                    if ((times - time2).TotalSeconds > 10)
                    {
                        time2 = times;
                        Console.WriteLine("10s " + curCount + "/" + localheight);
                    }
                }
                else
                {
                    //30秒刷新高度
                    DateTime time1 = DateTime.Now;
                    if ((time1 - time).TotalSeconds > 30)
                    {
                        localheight = await GetLocalHeight();
                        //serverCount = await client.GetBlockCountAsync();
                        time = time1;
                    }
                    System.Threading.Thread.Sleep(100);
                }
            }

        }

        private static async Task GetAddrOnBlock(uint height, Dictionary<string, bool> addrs, List<string> lines)
        {
            op.height = (int)height;
            var state = await neblocal.Api.GetBlockByHeight(op);
            var block = Newtonsoft.Json.Linq.JObject.Parse(state);
            if (block.ContainsKey("result") && (block["result"] as Newtonsoft.Json.Linq.JObject).ContainsKey("transactions"))
            {
                var trans = block["result"]["transactions"] as Newtonsoft.Json.Linq.JArray;
                //foreach (var tx in block.Transactions)
                foreach (var tx in trans)
                {
                    if ((tx as Newtonsoft.Json.Linq.JObject).ContainsKey("to"))
                    {
                        var to = tx["to"].ToString();
                        if (to != null)
                        {
                            if (addrs.ContainsKey(to) == false)
                            {

                                addrs[to] = true;
                                lines.Add(to);
                            }
                        }
                    }
                }
            }




        }

    }
}
